#include "scene_main.h"
#include "SDL_image.h"

SceneMain::SceneMain() : game_(Game::getInstance())
{
}

SceneMain::~SceneMain()
{

}

void SceneMain::init()
{
    /// 随机数
    std::random_device rd;
    gen_ =  std::mt19937(rd());
    dis_ =  std::uniform_real_distribution<float>(0, 1);

    player_.ptexture_ = IMG_LoadTexture(game_.getRenderer(), "assets/image/SpaceShip.png");
    SDL_QueryTexture(player_.ptexture_, NULL, NULL, &player_.width_, &player_.height_);
    player_.width_ /= 4;
    player_.height_ /= 4;
    player_.position_.x = game_.getWindowWidth() / 2 - player_.width_ / 2;
    player_.position_.y = game_.getWindowHeight()  - player_.height_ ;

    /// 初始化子弹模板
    projectileTemplate_.ptexture_ = IMG_LoadTexture(game_.getRenderer(), "assets/image/laser-1.png");
    SDL_QueryTexture(projectileTemplate_.ptexture_, NULL, NULL, &projectileTemplate_.width_, &projectileTemplate_.height_);
    projectileTemplate_.width_ /= 4;
    projectileTemplate_.height_ /= 4;

    enemyTemplate_.ptexture_ = IMG_LoadTexture(game_.getRenderer(), "assets/image/insect-1.png");
    SDL_QueryTexture(enemyTemplate_.ptexture_, NULL, NULL, &enemyTemplate_.width_, &enemyTemplate_.height_);
    enemyTemplate_.width_ /= 4;
    enemyTemplate_.height_ /= 4;

    projectileEnemyTemplate_.ptexture_ = IMG_LoadTexture(game_.getRenderer(), "assets/image/laser-2.png");
    SDL_QueryTexture(projectileEnemyTemplate_.ptexture_, NULL, NULL, &projectileEnemyTemplate_.width_, &projectileEnemyTemplate_.height_);
    projectileEnemyTemplate_.width_ /= 4;
    projectileEnemyTemplate_.height_ /= 4;

    /// initialize explosion template
    explosionTemplate_.ptexture_ = IMG_LoadTexture(game_.getRenderer(), "assets/effect/explosion.png");
    SDL_QueryTexture(explosionTemplate_.ptexture_, NULL, NULL, &explosionTemplate_.width_, &explosionTemplate_.height_);
    explosionTemplate_.current_frame_ = 0;
    explosionTemplate_.total_frames_ = explosionTemplate_.width_ / explosionTemplate_.height_;
    explosionTemplate_.width_ = explosionTemplate_.height_;
    
}

void SceneMain::clean()
{
    for (auto projectile : projectiles_)    
    {
        if (projectile != NULL)
            delete projectile;
    }
    projectiles_.clear();

    for ( auto enemy : enemies_)    
    {
        if (enemy != NULL)
            delete enemy;
    }
    enemies_.clear();
    
    for (auto projectile_enemy : projectiles_enemy_)
    {
        if (projectile_enemy != NULL)
            delete projectile_enemy;
    }
    projectiles_enemy_.clear();


    for (auto explosion : explosions_)
    {
        if (explosion != NULL)
            delete explosion;
    }

    /// 清除模板
    if (explosionTemplate_.ptexture_ != NULL)
    {
        SDL_DestroyTexture(explosionTemplate_.ptexture_);
    }
    

    if (projectileTemplate_.ptexture_ != NULL)
    {
        SDL_DestroyTexture(projectileTemplate_.ptexture_);
    }

    if (enemyTemplate_.ptexture_ != NULL)
    {
        SDL_DestroyTexture(enemyTemplate_.ptexture_);
    }
 
    if (projectileEnemyTemplate_.ptexture_ != NULL)
    {
        SDL_DestroyTexture(projectileEnemyTemplate_.ptexture_);
    }

    if (player_.ptexture_ != NULL) 
    {
        SDL_DestroyTexture(player_.ptexture_);
    }
}

void SceneMain::handleEvent(SDL_Event event)
{

}

void SceneMain::update(float deltaTime)
{
    keyboardControls(deltaTime);
    updateProjectilesPlayer(deltaTime);
    updateProjectilesEnemy(deltaTime);
    updateEnemies(deltaTime);
    updatePlayer(deltaTime);
    updateExplosions(deltaTime);
}

void SceneMain::render()
{
    renderProjectilesPlayer();
    renderProjectilesEnemy();
        
    if (!player_.is_dealt_)
    {
        SDL_Rect playerRect = { static_cast<int>(player_.position_.x), 
                                static_cast<int>(player_.position_.y), 
                                player_.width_, 
                                player_.height_ };

        SDL_RenderCopy(game_.getRenderer(), player_.ptexture_, NULL, &playerRect);   
    }

    renderEnemies();   
    renderExplosions();
}

void SceneMain::keyboardControls(float deltaTime)
{
    if (player_.is_dealt_)
    {
        return;
    }
    

    auto keyState = SDL_GetKeyboardState(NULL);
    if (keyState[SDL_SCANCODE_W] == SDL_PRESSED)
    {
        player_.position_.y -= deltaTime * player_.speed_;
    }   
    if (keyState[SDL_SCANCODE_S] == SDL_PRESSED)
    {
        player_.position_.y += deltaTime * player_.speed_;
    }   
    if (keyState[SDL_SCANCODE_A] == SDL_PRESSED)
    {
        player_.position_.x -= deltaTime * player_.speed_;
    }
    if (keyState[SDL_SCANCODE_D] == SDL_PRESSED)
    {
        player_.position_.x += deltaTime * player_.speed_;
    }

    if (player_.position_.x < 0)
    {
        player_.position_.x = 0;
    }
    if (player_.position_.y < 0)
    {
        player_.position_.y = 0;
    }
    if (player_.position_.x > game_.getWindowWidth() - player_.width_)
    {
        player_.position_.x = game_.getWindowWidth() - player_.width_;
    }
    if (player_.position_.y > game_.getWindowHeight() - player_.height_)
    {
        player_.position_.y = game_.getWindowHeight() - player_.height_;
    }

    /// 发射子弹
    if (keyState[SDL_SCANCODE_J] == SDL_PRESSED)
    {
        auto current_time = SDL_GetTicks();
        if (current_time - player_.last_shot_time_ > player_.cool_down_)
        {
            shootPlayer();
            player_.last_shot_time_ = current_time;
        }
    } 

    ///
    spawnEnemy();

}

void SceneMain::shootPlayer()
{   
    if (player_.hp_ <= 0)
    {
        return;
    }


    /// 首先创建对象插入列表
    ProjectilePlayer* projectile = new ProjectilePlayer(projectileTemplate_);
    projectile->position_.x = player_.position_.x + player_.width_ / 2 - projectile->width_ / 2;
    projectile->position_.y = player_.position_.y;
    
    projectiles_.push_back(projectile);

}

void SceneMain::shootEnemy(Enemy &enemy)
{
    ProjectileEnemy* projectile_enemy = new ProjectileEnemy(projectileEnemyTemplate_);
    projectile_enemy->position_.x = enemy.position_.x + enemy.width_ / 2 - projectile_enemy->width_ / 2;
    projectile_enemy->position_.y = enemy.position_.y + enemy.height_/2 - projectile_enemy->height_/ 2;
    projectile_enemy->direction_ = getDirection(enemy);
    projectiles_enemy_.push_back(projectile_enemy);
}

void SceneMain::updateProjectilesPlayer(float deltaTime)
{
    for (auto it = projectiles_.begin(); it != projectiles_.end(); )
    {
        auto projectile = *it;
        projectile->position_.y -= projectile->speed_ * deltaTime;
        
        if (projectile->position_.y < 0)
        {
            delete projectile;
            it = projectiles_.erase(it);
        }
        else 
        {

            bool hit = false;
            /// 检测碰撞
            for (auto enemy : enemies_)
            {
               
                SDL_Rect projectile_rect = { static_cast<int>(projectile->position_.x),
                                            static_cast<int>(projectile->position_.y),
                                            projectile->width_,
                                            projectile->height_ };

                SDL_Rect enemy_rect = { static_cast<int>(enemy->position_.x),
                                        static_cast<int>(enemy->position_.y),            
                                        enemy->width_, 
                                        enemy->height_ };

                if (SDL_HasIntersection(&projectile_rect, &enemy_rect)) 
                {
                    hit = true;
                    enemy->hp_--;
                    delete projectile;
                    it = projectiles_.erase(it);
                    break;
                }
                
            }
            if (!hit)
            {
                it++;   
            } 
            
        }
    }

    
}

void SceneMain::updateProjectilesEnemy(float deltaTime)
{
    for (auto it = projectiles_enemy_.begin(); it != projectiles_enemy_.end(); )
    {
        auto projectile = *it;
        projectile->position_.x += projectile->direction_.x * projectile->speed_ * deltaTime;
        projectile->position_.y += projectile->direction_.y * projectile->speed_ * deltaTime;

        if (projectile->position_.y > game_.getWindowHeight() ||
            projectile->position_.x < 0 || 
            projectile->position_.x > game_.getWindowWidth()||
            projectile->position_.y < 0)
        {
            delete projectile;
            it = projectiles_enemy_.erase(it);
        }
        else 
        {

            SDL_Rect projectile_rect = { static_cast<int>(projectile->position_.x),
                                        static_cast<int>(projectile->position_.y),
                                        projectile->width_,
                                        projectile->height_ };

            SDL_Rect player_rect = { static_cast<int>(player_.position_.x),
                                    static_cast<int>(player_.position_.y),
                                    player_.width_,
                                    player_.height_ };  

            if (SDL_HasIntersection(&projectile_rect, &player_rect) && !player_.is_dealt_)
            {
                player_.hp_--;
                delete projectile;
                it = projectiles_enemy_.erase(it); 
            }
            else
            {
                it++;
            }
        }
    }
}

void SceneMain::renderProjectilesEnemy()
{
    for (auto projectile : projectiles_enemy_)
    {
        SDL_Rect projectile_rect = { static_cast<int>(projectile->position_.x), 
                                    static_cast<int>(projectile->position_.y), 
                                    projectile->width_, 
                                    projectile->height_ };

        /// SDL_RenderCopy(game_.getRenderer(), projectile->ptexture_, NULL, &projectile_rect);
        double angle = atan2(projectile->direction_.y, projectile->direction_.x) * 180 / M_PI - 90;
        SDL_RenderCopyEx(game_.getRenderer(), projectile->ptexture_, NULL, &projectile_rect, angle, NULL, SDL_FLIP_NONE);

    }
    
}


void SceneMain::renderProjectilesPlayer()
{
    for (auto projectile : projectiles_)
    {
        SDL_Rect projectile_rect = { static_cast<int>(projectile->position_.x), 
                                    static_cast<int>(projectile->position_.y), 
                                    projectile->width_, 
                                    projectile->height_ };

        SDL_RenderCopy(game_.getRenderer(), projectile->ptexture_, NULL, &projectile_rect);
    }
}


SDL_FPoint SceneMain::getDirection(Enemy &enemy)
{
    auto x = player_.position_.x + player_.width_ / 2 - enemy.position_.x - enemy.width_ / 2;
    auto y = player_.position_.y + player_.height_ / 2 - enemy.position_.y - enemy.height_ / 2;
    /// 归一化
    auto length = sqrt(x * x + y * y);
    x /= length;
    y /= length;
    return SDL_FPoint{x, y};
}

void SceneMain::spawnEnemy()
{
    /// 生成概率
    if (dis_(gen_) > 1 / 60.0f)
    {
        return;
    }

    /// 生成敌人
    Enemy* enemy = new Enemy(enemyTemplate_);
    enemy->position_.x = dis_(gen_) * (game_.getWindowWidth() - enemy->width_);
    enemy->position_.y = -enemy->height_;
    enemies_.push_back(enemy);

}



void SceneMain::updateEnemies(float deltaTime)
{
        for (auto it = enemies_.begin(); it != enemies_.end(); )
        {

            auto current_time = SDL_GetTicks();
            auto enemy = *it;
            enemy->position_.y += enemy->speed_ * deltaTime;

            if (enemy->position_.y > game_.getWindowHeight())
            {
                delete enemy;
                it = enemies_.erase(it);
            }
            else 
            {
                if (current_time - enemy->last_shot_time_ > enemy->cool_down_)
                {
                    shootEnemy(*enemy);
                    enemy->last_shot_time_ = current_time;
                }
                if (enemy->hp_ <= 0)
                {
                    ///delete enemy;
                    enemyExplosion(enemy);
                    it = enemies_.erase(it);
                }
                else 
                {
                    it++;
                }
            }
        }
}

void SceneMain::renderEnemies()
{
    for (auto enemy  :  enemies_)
    {
        SDL_Rect enemy_rect = { static_cast<int>(enemy->position_.x), 
                                    static_cast<int>(enemy->position_.y), 
                                    enemy->width_, 
                                    enemy->height_ };

        SDL_RenderCopy(game_.getRenderer(), enemy->ptexture_, NULL, &enemy_rect);
    }
}


void SceneMain::updatePlayer(float deltaTime)
{
    if (player_.is_dealt_)
    {
        /* code */
        return;
    }
    

    if (player_.hp_ <= 0)
    {
        player_.is_dealt_ = true;  
        auto explosion = new Explosion(explosionTemplate_);
        explosion->position_.x = player_.position_.x + player_.width_ / 2 - explosion->width_ / 2;
        explosion->position_.y = player_.position_.y + player_.height_ / 2 - explosion->height_ / 2;
        explosion->start_time_ = SDL_GetTicks();
        explosions_.push_back(explosion);
        return;
    } 

    /// check 
    for (auto enemy : enemies_)
    {

        SDL_Rect enemy_rect = { static_cast<int>(enemy->position_.x),
                                    static_cast<int>(enemy->position_.y),
                                    enemy->width_,  
                                    enemy->height_ };
    

        SDL_Rect player_rect  = { static_cast<int>(player_.position_.x),    
                                    static_cast<int>(player_.position_.y),
                                    player_.width_,
                                    player_.height_ };


        if (SDL_HasIntersection(&player_rect, &enemy_rect))
        {
            player_.hp_--;
            enemy->hp_ = 0;
        }
    }
}

void SceneMain::updateExplosions(float)
{
    auto current_time = SDL_GetTicks();
    for (auto it = explosions_.begin(); it != explosions_.end(); )
    {
        auto explosion = *it;
        explosion->current_frame_ = (current_time - explosion->start_time_) * explosion->FPS_ / 1000;
        if (explosion->current_frame_ >= explosion->total_frames_)
        {
            delete explosion;
            it = explosions_.erase(it);
        }
        else
        {
            it++;
        }
    }
}

void SceneMain::renderExplosions()
{
    for (auto explosion : explosions_)
    {
        SDL_Rect src = { static_cast<int>(explosion->current_frame_ * explosion->width_), 
                            0,
                            explosion->width_, 
                            explosion->height_ };
        SDL_Rect dst =  {static_cast<int>(explosion->position_.x), 
                        static_cast<int>(explosion->position_.y), 
                        explosion->width_, 
                        explosion->height_ };

        SDL_RenderCopy(game_.getRenderer(), explosion->ptexture_, &src, &dst);
    }
}


void SceneMain::enemyExplosion(Enemy *enemy)
{
    auto explosion = new Explosion(explosionTemplate_);
    explosion->position_.x = enemy->position_.x + enemy->width_ / 2 - explosion->width_ / 2;
    explosion->position_.y = enemy->position_.y + enemy->height_ / 2 - explosion->height_ / 2;
    explosion->start_time_ = SDL_GetTicks();
    explosions_.push_back(explosion);            
    delete enemy;
}
